﻿using System;
using System.Collections.Generic;
using System.Resources;
using System.Security;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading.Tasks;
using Lextm.SharpSnmpLib;
using ServerLib.Entities;
using monitor_2_DBwork;
using Client_Server_Interface;
using SResp=Client_Server_Interface.ServerResponceTypes;

namespace ServerLib
{
    public delegate void VoidStringDelegate(string a);

    public delegate void VoidDelegate();

    public static class Server
    {
        public const string CurrentIpToListenValueName = "SNMP_Listen_ip";
        public const string ServerLogLastRecordsCountOptionName = "ServerLogLastRecordsCount";
        private const string DoReturnString = "do_return";
        public static event UsersListChangedDelegate UsersListChangedEvent;
        public static event NewLogRecordDelegate NewLogRecordEvent;

        //private delegate void VoidStringDelegate(string a);

        private static void OnNewLogRecordEvent(string login, string access, string region, string category, string eventname, DateTime date)
        {
            var handler = NewLogRecordEvent;
            if (handler != null) handler(login, access, region, category, eventname, date);
        }

        //Срабатывает при изменении списка пользователей
        private static void OnUsersListChangedEvent(string login, string access, string region, DateTime loggedIn, IPAddress ip, bool added)
        {
            var handler = UsersListChangedEvent;
            if (handler != null) handler(login, access, region, loggedIn.ToString("M/d H:m:s"), ip.ToString(), added);
        }

        public static event VoidDelegate SaveLogsComplete;

        public static void OnSaveLogsComplete()
        {
            VoidDelegate handler = SaveLogsComplete;
            if (handler != null) handler();
        }

        public static event VoidDelegate BizssControlComplete;

        public static void OnBizssControlComplete()
        {
            VoidDelegate handler = BizssControlComplete;
            if (handler != null) handler();
        }

        private static Mutex _superMutex;

        private const string Version = "btm1.0";
        private static Socket _clientListener;
        private const int LocalPort = 38001;
        private static bool _isStarted;
        private static bool _isInitialized;
        private static readonly Encoding MessagesEncoding;// = Encoding.GetEncoding("Windows-1251");
        private const string UnknownString = "unknown";

        private const bool CheckClients = false;
        private static readonly ClientsChecker ClientChecker;

        public static class DeviceLibraries
        {
            private static Dictionary<string, DllUnmanagedWork> _devLibraries;
            private static Dictionary<IPAddress, DllUnmanagedWork> _ipLibraries;

            private static bool _initSuccess;
            
            public static bool Initialize()
            {
                var deviceLibs = DBwork.GetDeviceLibsList();
                // 0 - ip
                // 1 - lib name
                // 2 - dev name
                // 3 - rtps name
                if (deviceLibs.Count < 1)
                {
                    WriteToLog(Rm.GetString("NoActiveDevices"), ServerCategories.Info);
                    return _initSuccess;
                }
                if (deviceLibs[0][0] == "err:")
                {
                    WriteToLog(deviceLibs[0][1], ServerCategories.Warning);
                    return _initSuccess;
                }

                _devLibraries = new Dictionary<string, DllUnmanagedWork>();
                _ipLibraries = new Dictionary<IPAddress, DllUnmanagedWork>();

                foreach (var device in deviceLibs)
                {
                    //Создается список "название устройства - библиотека"
                    if (!_devLibraries.ContainsKey(device[2]))
                    {
                        _devLibraries.Add(device[2], new DllUnmanagedWork(device[1], device[2]));
                    }

                    //Создается список "ip - библиотека"
                    IPAddress ip;
                    try
                    {
                        ip = IPAddress.Parse(device[0]);
                    }
                    catch (Exception ex)
                    {
                        WriteToLog("err:" + ex.Message + ": " + device[0], ServerCategories.Warning);
                        return _initSuccess;
                    }
                    
                    if (!_ipLibraries.ContainsKey(ip))
                    {
                        _ipLibraries.Add(ip, _devLibraries[device[2]]);
                    }
                }
                return _initSuccess = true;
            }

            public static void Reset()
            {
                _initSuccess = false;
                foreach (var dllUnmanagedWork in _devLibraries)
                {
                    var str = dllUnmanagedWork.Value.Flush();
                    if (str != "ok")
                    {
                        var err = str.Contains("err:") ? str.Remove(0, 4) : str;
                        WriteToLog(dllUnmanagedWork.Value.DeviceName + ": " + err, ServerCategories.Warning);
                        //return;
                    }
                }
            }

            public static DllUnmanagedWork GetByDeviceName(string name)
            {
                if(!_initSuccess)
                    if(!Initialize())
                        throw new Exception("not_initialized");
                return _devLibraries.ContainsKey(name) ? _devLibraries[name] : null;
            }

            public static DllUnmanagedWork GetByIp(IPAddress ip)
            {
                if (!_initSuccess)
                    throw new Exception("not_initialized");
                return _ipLibraries.ContainsKey(ip) ? _ipLibraries[ip] : null;
            }
        }

        public delegate void EventToLogDelegate(string message, ServerCategories category, bool warning);
        private static event EventToLogDelegate WriteToLogEvent;

        static Server()
        {
            WriteToLogEvent += Server_WriteToLogSystemEvent;
            //_assemblyIdentifier = new HardIdentifier();
            ClientChecker = new ClientsChecker();
            MessagesEncoding = Encoding.GetEncoding("Windows-1251");
        }

        public static string Init(NotifyIcon icon)
        {
            var dir = CurDir.Get;
            if (!File.Exists(@"Resources\Libs\monitor_2_DBwork.dll") && !File.Exists(@"monitor_2_DBwork.dll"))
                return "err:" + Rm.GetString("DllIsNotExist") + @": " + "monitor_2_DBwork.dll";
            if (icon == null)
            {
                //return "err:icon = null";
            }
            else
            {
                _nIcon = icon;
            }
                
            if (!DBwork.CheckConnection())
                return "err:" + Rm.GetString("NoDBConnection");
            
            if (!DeviceLibraries.Initialize())
                return "err:" + Rm.GetString("LibrariesInitError");
            if(CheckClients)
                if (ClientChecker.Init() != "ok")
                    return "err:" + Rm.GetString("ClientCheckerInitError");
            _isInitialized = true;
            return "ok";
        }

        public static string WriteToLog(string login, string access, string region, ServerCategories category, string message, DateTime date)
        {
            string ac = access;
            int acChk;
            if (int.TryParse(access, out acChk))
                ac = DBwork.GetAccessType(acChk);
            OnNewLogRecordEvent(login, ac, region, category.ToString(), message, date);
            message = message.Replace(';', '.');
            if (message.StartsWith("err:"))
            {
                message = message.Remove(0, 4);
            }
            else if (message.StartsWith("serr:"))
            {
                message = message.Remove(0, 5);
            }
            return DBwork.SaveServerLog(login, access, region, (int)category, message, date);
            //if (handler != null) handler(message, category, warning);
        }

        public static void WriteToLog(string message, ServerCategories category)
        {
            WriteToLog("System", "System", "Local", category, message, DateTime.Now);
            
        }
        
        
        public static readonly ResourceManager Rm = new ResourceManager(typeof(ServerLib));
        private static NotifyIcon _nIcon;

        private static void ShowNIcon(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
        {
            if(_nIcon != null)
                _nIcon.ShowBalloonTip(timeout,tipTitle,tipText, tipIcon);
        }
        

        class ConnectionInfo : IDisposable
        {
            //private static List<string> _admins;
            //private static List<string> _users;

            //public ConnectionInfo()
            //{
            //    _admins = new List<string> { "Администратор", "Administrator" };
            //    _users = new List<string> { "Пользователь", "User" };
            //}

            public ConnectionInfo(Socket soc)
            {
                Socket = soc;
                Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                Buffer = new byte[3000];
                IsAuthorized = false;
                var t = new Task(CheckingTask);
                Group = UnknownString;
                GroupId = UnknownString;
                t.Start();
            }

            //Проверка пользователя в сети
            private void CheckingTask()
            {
                while (true)
                {
                    SyncMutex.WaitOne();
                    if (!sync.TaskWork)
                    {
                        SyncMutex.ReleaseMutex();
                        return;
                    }
                    try
                    {
                        if (Socket.Poll(1, SelectMode.SelectRead) && Socket.Available == 0)
                            //Можно словить NullException
                        {
                            SyncMutex.ReleaseMutex();
                            CloseConnection(this);
                            return;
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        SyncMutex.ReleaseMutex();
                        return;
                    }
                    SyncMutex.ReleaseMutex();
                    //try
                    //{
                    //    if (Socket.Poll(1, SelectMode.SelectRead) && Socket.Available == 0)
                    //    {
                    //        CloseConnection(this);
                    //        return;
                    //    }
                    //}
                    //catch (Exception)
                    //{
                    //    CloseConnection(this);
                    //    return;
                    //}
                    Thread.Sleep(1000);
                    //if (Socket.Connected == false)
                    //    return;
                }
            }


            public Socket Socket;
            public byte[] Buffer;
            public string Login = UnknownString;
            //public string Group = UnknownString;
            //public string GroupId = UnknownString;
            private string _groupId;
            public string GroupId {
                get { return _groupId; }
                set
                {
                    if(string.IsNullOrEmpty(value))
                        return;
                    switch (value)
                    {
                        case "1":
                            Permissions = PermissionsHelper.AdminPermissionTypes;
                            break;
                        case "2":
                            Permissions = PermissionsHelper.UserPermissionTypes;
                            break;
                        default:
                            Permissions = PermissionTypes.None;
                            break;
                    }
                    _groupId = value;
                } }
            public string Group { get; set; }
            public string Oblast = UnknownString;
            public byte[] Id;
            public IPAddress Ip { get; set; }
            public DateTime Date { get; set; }

            public PermissionTypes Permissions;

            public Mutex SyncMutex = new Mutex();
            public Sync sync = new Sync();
            public class Sync
            {
                public bool TaskWork = true;
            }

            public bool IsAuthorized { get; set; }

            public bool IsAdmin
            {
                get
                {
                    if (GroupId == "1")
                        return true;
                    return false;
                }
            }

            public bool IsUser
            {
                get
                {
                    if (GroupId == "2")
                        return true;
                    return false;
                }
            }

            public void Dispose()
            {
                lock (sync)
                {
                    sync.TaskWork = false;
                }
                try
                {
                    Socket.Shutdown(SocketShutdown.Both);
                }
                catch (Exception)
                {
                    
                }
                finally
                {
                    Socket.Close();
                }
                
                
            }
        }

        static private readonly List<ConnectionInfo> Connections = new List<ConnectionInfo>();

        public static string ServerStart(NotifyIcon icon)
        {
            if (!_isInitialized)
            {
                var chk = Init(icon);
                if (chk != "ok")
                    return chk;
            }
            return ServerStart();
        }

        private static string ServerStart()
        {
            bool isNewMutex;
            _superMutex = new Mutex(true, "DVB-TesS_(DVB-TESS)_server", out isNewMutex);
            if (!isNewMutex)
            {
                _superMutex.Close();
                return "err:" + Rm.GetString("ServerON");
            }
                
            if (!_superMutex.WaitOne(200))
            {
                _superMutex.Close();
                return "err:" + Rm.GetString("ServerON");
            }
                
            if (!_isInitialized)
            {
                _superMutex.Close();
                return "err:not initialized";
            }
            //Запуск сохранения логов
            SaveLogs.Start();

            //Запуск контроля измерителей
            BizssControl.Start();

            //Запуск чат сервера
            try
            {
                ChatServer.Start();
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message, ServerCategories.Warning);
                //MessageBox.Show(ex.Message, Rm.GetString("Warning"));
            }
            

            //Прослушка traps
            /*var chk = SnmpTrapsListener.Start();

            if (chk != "ok")
            {
                var d = new VoidStringDelegate(ShowMessageBox);
                d.BeginInvoke(chk.Remove(0, 4), null, null);
                //MessageBox.Show(chk.Remove(0, 4) + ". Прослушка SNMP ловушек осуществляться не будет.", Rm.GetString("Warning"), MessageBoxButtons.OK,
                //                MessageBoxIcon.Warning);

            }*/

            //Для проверки сигнатур клиентов
            ClientChecker.Init();

            var localEp = new IPEndPoint(IPAddress.Any, LocalPort);
            _clientListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _clientListener.Bind(localEp);
            _clientListener.Listen(10);
            //for (int i = 0; i < 10; i++)
            //{
            _clientListener.BeginAccept(Accept_handle, _clientListener);
            //}

            string date = DateTime.Now.Year + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day + " " +
                            DateTime.Now.ToLongTimeString();
            WriteToLog("System", "System", "Local", ServerCategories.Info, Rm.GetString("ServerStarted"),
                        DateTime.Now);
            if (_nIcon != null)
                _nIcon.Text = Rm.GetString("ServerON");
            ShowNIcon(2000, Rm.GetString("ServerStarted"), date, ToolTipIcon.Info);
            _isStarted = true;
            return "ok";
        }

        static void ShowMessageBox(string message)
        {
            MessageBox.Show(message + ". Прослушка SNMP ловушек осуществляться не будет.", Rm.GetString("Warning"), MessageBoxButtons.OK,
                                   MessageBoxIcon.Warning);   
        }

        public static string ServerStop()
        {
            try
            {
                _superMutex.Close();
            }
            catch (Exception)
            {
                //_superMutex.Close();
            }
            if (!_isStarted)
                return "ok";

            if(CheckClients)
                ClientChecker.Reset();

            //SnmpTrapsListener.Stop();

            SaveLogs.Stop();
            SaveLogs.Reset();

            DeviceLibraries.Reset();

            BizssControl.Stop();
            BizssControl.Reset();

            ChatServer.Stop();
            

            int j = 0;
            foreach (ConnectionInfo con in Connections)
            {
                OnUsersListChangedEvent(con.Login, con.Group, con.Oblast, con.Date, con.Ip, false);
                con.Socket.Shutdown(SocketShutdown.Both);
                con.Socket.Close();
                j++;
            }
            Connections.RemoveRange(0, j);

            //if (_bizssControlThread != null && _bizssControlThread.IsAlive)
            //{
            //    _bizssControlThread.Abort();
            //    _bizssControlThread.Join(2000);
            //}


            string date = DateTime.Now.Year + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day + " " + DateTime.Now.ToLongTimeString();
            WriteToLog("System", "System", "Local", ServerCategories.Info, Rm.GetString("ServerStopped"), DateTime.Now);
            if (_nIcon != null)
            {
                _nIcon.Text = Rm.GetString("ServerOFF");
                ShowNIcon(2000, Rm.GetString("ServerStopped"), date, ToolTipIcon.Info);
            }
            if(_clientListener != null)
                _clientListener.Close();
            _isInitialized = false;
            return "ok";
        }

        static void Server_WriteToLogSystemEvent(string message, ServerCategories category, bool warning)
        {
            //MessageBox.Show(message);
            //string str = DBwork.SaveServerLog("System", "", "", System.Text.Encoding.UTF8.GetString(System.Text.Encoding.Default.GetBytes(message)), DateTime.Now);
            
            string str = DBwork.SaveServerLog("System", "System", "Local", (int)category, message, DateTime.Now);
            if (_nIcon == null)
                return;
            if (warning)
            {
                ToolTipIcon icon;
                switch (category)
                {
                    case ServerCategories.Info:
                        icon = ToolTipIcon.Info;
                        break;
                    case ServerCategories.Warning:
                        icon = ToolTipIcon.Warning;
                        break;
                    default:
                        icon = ToolTipIcon.None;
                        break;
                }
                if (!_nIcon.ContextMenuStrip.Visible)
                    ShowNIcon(2000, Rm.GetString("Warning"), message, icon);
            }
            
        }

        

        static void Accept_handle(IAsyncResult ar)                             //Accept
        {

            //var connection = new ConnectionInfo();
            var listener = (Socket)ar.AsyncState;

            try
            {
                var connection = new ConnectionInfo(listener.EndAccept(ar));
                //connection.Socket = listener.EndAccept(ar);
                //connection.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                //connection.Buffer = new byte[100];
                lock (Connections) Connections.Add(connection);
                connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, ReceiveCallback, connection);
                _clientListener.BeginAccept(Accept_handle, ar.AsyncState);
            }
            catch
            {
            }
        }

        static void ReceiveCallback(IAsyncResult result)                       //Получение пакета
        {
            var connection = (ConnectionInfo)result.AsyncState;
            int bytesRec;
            try
            {
                bytesRec = connection.Socket.EndReceive(result);
            }
            catch (Exception)
            {
                return;
            }
            var remoteEp = (IPEndPoint)connection.Socket.RemoteEndPoint;                //Определение IP
            var ip = remoteEp.Address;
            connection.Ip = ip;
            var buff = new byte[bytesRec];
            for (int i = 0; i < bytesRec; i++)
            {
                buff[i] = connection.Buffer[i];
            }
            if (bytesRec == 0)
            {
                CloseConnection(connection);
                return;
            }

            RequestWork(buff, connection);

            try
            {
                connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, ReceiveCallback, connection);
            }
            catch (ObjectDisposedException)
            { }
            catch (Exception)
            {
                CloseConnection(connection);
            }

        }

        /// <summary>
        /// Определение типа запроса.
        /// </summary>
        /// <param name="recMessage">Принятое сообщение.</param>
        /// <param name="connection">Соединение, с помощью которого было принято сообщение.</param>
        static void RequestWork(byte[] recMessage, ConnectionInfo connection)
        {
            var message = MessagesEncoding.GetString(recMessage);
            string[] split = message.Split(':');
            if (split.Length < 2)
            {
                if (split[0] != ClientRequestTypes.CLIENT_DEVICE_TREE_REQUEST)
                    //connection.Socket.Send(MessagesEncoding.GetBytes("accessden:wrongstring"));
                    connection.Socket.Send(MessagesEncoding.GetBytes(SResp.ACCESS_DENIED  + SResp.WRONG_REQUEST_STRING));

                CloseConnection(connection);
                return;
            }
            string req = split[0];
            
            //ShowNIcon(2000, "Принято сообщение", message, ToolTipIcon.Info);
            try
            {
                switch (req)
                {
                        //case "login":
                    case ClientRequestTypes.CLIENT_LOGIN_REQUEST: //Запрос на вход в систему
                        int rlen = req.Length + 1;
                        var toWork = new byte[recMessage.Length - rlen];
                        Buffer.BlockCopy(recMessage, rlen, toWork, 0, recMessage.Length - rlen);
                        //message = split[1];
                        LoginResponse(toWork, connection);
                        break;
                        //case "devlogdata":
                    case ClientRequestTypes.CLIENT_LOGS_REQUEST: //Запрос журнала
                        message = message.Remove(0, req.Length + 1);
                        DeviceLogDataResponse(message, connection);
                        break;
                    case ClientRequestTypes.CLIENT_DEVICE_TREE_REQUEST: //Запрос дерева устройств
                        //message = message.Remove(0, req.Length + 1);
                        DeviceTreeResponse(connection);
                        break;
                    case ClientRequestTypes.CLIENT_LOGIN_PERMISSIONS_REQUEST:
                        PermissionsResponse(connection);
                        break;
                    default:
                        //connection.Socket.Send(MessagesEncoding.GetBytes("accessden:notimpl"));
                        connection.Socket.Send(
                            MessagesEncoding.GetBytes(SResp.ACCESS_DENIED +
                                                      SResp.NOT_IMPLEMENTED_REQUEST));
                        CloseConnection(connection);
                        //CloseConnection(connection);
                        break;
                }
            }
            catch (Exception ex)
            {
                //toImplement
                //connection.Socket.Send(Encoding.Default.GetBytes("err:innererror"));
            }

        }

        private static void PermissionsResponse(ConnectionInfo connection)
        {
            var ps = connection.Permissions;
            var strToSend = PermissionsHelper.PermissionsToString(ps);
            var xw = XmlWriterHelper.CreateInstance(ClientRequestTypes.CLIENT_LOGIN_PERMISSIONS_REQUEST);
            xw.AddAttribute(typeof(PermissionTypes).ToString(), strToSend);
            var toSend = xw.EndElement();
            xw.Dispose();
            connection.Socket.Send(MessagesEncoding.GetBytes(toSend));
        }

        ///// <param name="message">Данные, присланные для входа. Без идентификатора типа запроса.</param>
        /// <summary>
        /// Обработка запроса на вход по идентификатору 'login'.
        /// </summary>
        /// <param name="data">Данные, присланные для входа. Без идентификатора типа запроса.</param>
        /// <param name="connection">Соединение, с помощью которого было принято сообщение.</param>
        static void LoginResponse(byte[] data, ConnectionInfo connection)
        {

            var message = MessagesEncoding.GetString(data);
            //ip-адрес клиента
            //var remoteEp = (IPEndPoint)connection.Socket.RemoteEndPoint;
            //var ip = remoteEp.Address;

            var ip = connection.Ip;

            //Определение данных для входа
            string login;
            string pw;
            string version;

            //string clientId;
            //string serverId = "";

            string[] split = message.Split(';');
            try
            {
                login = split[0];
                pw = split[1];
                version = split[2];
            }
            catch (IndexOutOfRangeException)
            {
                //connection.Socket.Send(Encoding.Default.GetBytes("accessden:wrongstring"));
                connection.Socket.Send(Encoding.Default.GetBytes(SResp.ACCESS_DENIED + SResp.WRONG_REQUEST_STRING));
                //Неверный формат строки
                CloseConnection(connection);
                return;
            }

            //Проверка версии
            if (version != Version)
            {
                ShowNIcon(2000, Rm.GetString("Warning"), Rm.GetString("WrongVersion") ?? "resources error", ToolTipIcon.Warning);
                //connection.Socket.Send(Encoding.Default.GetBytes("accessden:version"));
                connection.Socket.Send(Encoding.Default.GetBytes(SResp.ACCESS_DENIED + SResp.WRONG_VERSION));
                WriteToLog(login, "-", "-", ServerCategories.Login,
                                             Rm.GetString("WrongVersion") + ": " + version + ". " + ip, DateTime.Now);
                //DBwork.SaveServerLog(login, "-", "-", (int)ServerCategories.Login,
                //                             Rm.GetString("WrongVersion") + ": " + version + ". " + ip, DateTime.Now);
                //Несоответствие версий
                CloseConnection(connection);
                return;
            }

            if (Connections.Count > 3)
            {
                ShowNIcon(2000, Rm.GetString("Warning"), Rm.GetString("MaxConnection") ?? "resources error", ToolTipIcon.Warning);
                //connection.Socket.Send(Encoding.Default.GetBytes("accessden:connections_limit"));
                connection.Socket.Send(Encoding.Default.GetBytes( SResp.ACCESS_DENIED + SResp.CONNECTIONS_LIMIT));
                WriteToLog(login, "-", "-", ServerCategories.Login,
                                             Rm.GetString("MaxConnection") + ip, DateTime.Now);
                //Несоответствие версий
                CloseConnection(connection);
                return;
            }

            //Проверка сигнатуры клиента
            if (CheckClients)
            {
                if (split.Length > 3)
                {
                    int mlen = login.Length + pw.Length + version.Length + 3;
                    var clid = new byte[data.Length - mlen];        //client id
                    Buffer.BlockCopy(data, mlen, clid, 0, data.Length - mlen);
                    bool chk;
                    try
                    {
                        chk = ClientChecker.CheckClient(clid);
                    }
                    catch (Exception ex)
                    {
                        connection.Socket.Send(Encoding.Default.GetBytes(SResp.ACCESS_DENIED + SResp.INNER_ERROR));
                        WriteToLog(login, "-", "-", ServerCategories.Login,
                                             ex.Message + ". " + ip, DateTime.Now);
                        CloseConnection(connection);
                        return;
                    }
                    

                    //Неизвестный ключ
                    if (!chk)
                    {
                        ShowNIcon(2000, Rm.GetString("Warning"), Rm.GetString("UnauthorizedAttempt") ?? "resources error", ToolTipIcon.Warning);
                        connection.Socket.Send(Encoding.Default.GetBytes(SResp.ACCESS_DENIED + SResp.UNKNOWN_CLIENT_ID));
                        WriteToLog(login, "-", "-", ServerCategories.Login,
                                             Rm.GetString("ClientWithUnknownId") + ". " + ip, DateTime.Now);
                        CloseConnection(connection);
                        return;
                    }
                    bool uniqueId = true;
                    foreach (var con in Connections)
                    {
                        if (con.Login != UnknownString)
                        {
                            if (LicHelper.CompareByteArrays(con.Id, clid))
                                uniqueId = false;
                        }
                    }
                    if (uniqueId)
                    {
                        connection.Id = clid;
                    }
                    //else
                    //{
                    //    ShowNIcon(2000, Rm.GetString("Warning"), Rm.GetString("UnautorizedAttempt") ?? "resources error", ToolTipIcon.Warning);
                    //    connection.Socket.Send(Encoding.Default.GetBytes("accessden:not_unique_client_id"));
                    //    WriteToLog(login, "-", "-", ServerCategories.Login,
                    //                         Rm.GetString("NotUniqueClientId") + ". " + ip, DateTime.Now);
                    //    CloseConnection(connection);
                    //    return;
                    //}
                }
                //else
                //{
                //    ShowNIcon(2000, Rm.GetString("Warning"), Rm.GetString("UnautorizedAttempt") ?? "resources error", ToolTipIcon.Warning);
                //    connection.Socket.Send(Encoding.Default.GetBytes("accessden:unknown_client_id"));
                //    WriteToLog(login, "-", "-", ServerCategories.Login,
                //                         Rm.GetString("ClientWithUnknownId") + ". " + ip, DateTime.Now);
                //    CloseConnection(connection);
                //    return;
                //}
            }

            //Проверка по списку доступных адресов
            string obl = DBwork.CheckTrustedAddress(ip.ToString());
            if (obl == "notExist")
            {
                ShowNIcon(2000, Rm.GetString("Warning"), Rm.GetString("UnauthorizedAttempt") ?? "resources error", ToolTipIcon.Warning);
                connection.Socket.Send(Encoding.Default.GetBytes(SResp.ACCESS_DENIED + SResp.UNKNOWN_ADDRESS));
                WriteToLog(login, "-", "-", ServerCategories.Login,
                                     Rm.GetString("UnauthorizedAttempt") + ". " + ip, DateTime.Now);
                //WriteToLog();
                //Адрес отсутствует в списке доверенных
                CloseConnection(connection);
                return;
            }
            connection.Oblast = obl;
            
            //Проверка пароля
            //string group = DBwork.CallMethod("CheckPassword", login, pw);
            string group = DBwork.CheckPassword(login, pw);
            if (group.Contains("err:"))
            {
                ShowNIcon(2000, Rm.GetString("Warning") + "!", group.Substring(4), ToolTipIcon.Warning);
                connection.Socket.Send(Encoding.Default.GetBytes(SResp.ACCESS_DENIED + SResp.INNER_ERROR));
                WriteToLog(login, "-", "-", ServerCategories.SystemError,
                                     group.Remove(0, 4), DateTime.Now);
                //Какая ошибка на сервере
                CloseConnection(connection);
                return;
            }
            if (group == "0")
            {
                //Неверный логин или пароль
                WriteToLog(login, "", obl, ServerCategories.Login, Rm.GetString("WrongPassword") +": " + pw + ". IP: " + ip, DateTime.Now);
                connection.Socket.Send(Encoding.Default.GetBytes(SResp.ACCESS_DENIED+SResp.WRONG_LOGIN));
                CloseConnection(connection);
                return;
            }

            bool uniqueLogin = true;
            foreach (var con in Connections)
            {
                if (con.Login.Equals(login))
                    uniqueLogin = false;
            }
            if (!uniqueLogin)
            {
                ShowNIcon(2000, Rm.GetString("Warning"), Rm.GetString("UnauthorizedAttempt") ?? "resources error", ToolTipIcon.Warning);
                connection.Socket.Send(Encoding.Default.GetBytes(SResp.ACCESS_DENIED + SResp.LOGIN_IS_NOT_UNIQUE));
                WriteToLog(login, "-", "-", ServerCategories.Login,
                                     Rm.GetString("NotUniqueLogin") + ". " + ip, DateTime.Now);
                CloseConnection(connection);
                return;
            }

            var loggedTime = DateTime.Now;
            var access = DBwork.GetAccessType(int.Parse(group));

            OnUsersListChangedEvent(login, access, obl, loggedTime, ip, true);
            //Фиксирование данных о пользователе
            connection.Login = login;
            connection.Date = loggedTime;
            connection.GroupId = group;
            connection.Group = access;
            //connection.Ip = ip;
            connection.IsAuthorized = true;

            //Попытка чтения файла с устройствами
            var check = SendDeviceTree(connection);
            if(check == DoReturnString)
                return;

            //Успешный вход
            ShowNIcon(2000, Rm.GetString("UserEnterSuccess") + ": " + connection.Ip, login, ToolTipIcon.Info);
            check = WriteToLog(connection.Login, connection.GroupId, connection.Oblast, ServerCategories.Login,
                                        Rm.GetString("UserEnterSuccess") + ": " + connection.Ip, connection.Date);
            if (check != "ok")
            {
                ShowNIcon(2000, Rm.GetString("Warning"), check.Substring(4), ToolTipIcon.Warning);
            }
            
            
        }

        /// <summary>
        /// Формирует и отправляет дерево устройств.
        /// </summary>
        /// <param name="connection">Соединение с клиентом, через которое будет отправляться дерево устройств.</param>
        /// <returns>Строка-состояние.</returns>
        private static string SendDeviceTree(ConnectionInfo connection)
        {
            byte[] fileContent;
            //ToDo: выпилить эту херь
            const string filename = "deviceTree.xml";
            string oblast = connection.IsAdmin ? "all" : connection.Oblast;
            if (!GetFile(oblast, out fileContent))
            {
                //ShowNIcon(2000, Rm.GetString("CantOpenFile"), Directory.GetCurrentDirectory() + @"\resources\" + filename,
                //          ToolTipIcon.Warning);
                connection.Socket.Send(Encoding.Default.GetBytes("accessden:innererror"));
                //Не удалось открыть файл
                CloseConnection(connection);
                return DoReturnString;
            }

            byte[] len = BitConverter.GetBytes(fileContent.Length + filename.Length + 1);
            byte[] accessGranted = Encoding.Default.GetBytes("accessgranted:");

            var firstMessage = new byte[len.Length + accessGranted.Length];
            Buffer.BlockCopy(accessGranted, 0, firstMessage, 0, accessGranted.Length);
            Buffer.BlockCopy(len, 0, firstMessage, accessGranted.Length, len.Length);


            var nameLength = new byte[1];
            nameLength[0] = (byte) filename.Length;
            byte[] fileName = Encoding.Default.GetBytes(filename);

            var secondMessage = new byte[1 + fileName.Length + fileContent.Length];
            Buffer.BlockCopy(nameLength, 0, secondMessage, 0, nameLength.Length);
            Buffer.BlockCopy(fileName, 0, secondMessage, nameLength.Length, fileName.Length);
            Buffer.BlockCopy(fileContent, 0, secondMessage, nameLength.Length + fileName.Length, fileContent.Length);

            ////Успешный вход
            //string check = WriteToLog(connection.Login, connection.GroupId, connection.Oblast, ServerCategories.Login,
            //                          Rm.GetString("UserEnterSuccess") + ": " + connection.Ip, connection.Date);

            try
            {
                connection.Socket.Send(firstMessage);
                connection.Socket.Send(secondMessage);
            }
            catch (Exception ex)
            {
                ShowNIcon(2000, Rm.GetString("Warning"), ex.Message, ToolTipIcon.Warning);
            }
            //return check;
            return "ok";
        }

//Сохранять в лог все события!!

        /// <summary>
        /// Преобразует \resources\deviceTree.xml в массив байт.
        /// </summary>
        /// <param name="oblast">Область, для которой строится дерево.</param>
        /// <param name="fileContent">Выходной массив байт - содержание файла.</param>
        /// <returns>true - преобразование прошло успешно.</returns>
        static bool GetFile(string oblast ,out byte[] fileContent)
        {
            //string filePath = Directory.GetCurrentDirectory() + @"\resources\deviceTree.xml";
            //string chk = DBwork.TreeToXml(oblast, filePath);
            string nxml;
            var chk = DBwork.TreeToXml(oblast, out nxml);

            if (chk.Contains("err:"))
            {
                fileContent = new byte[0];
                return false;
            }
            try
            {
                //fileContent = File.ReadAllBytes(filePath);
                fileContent = Encoding.UTF8.GetBytes(nxml);
            }
            catch (Exception)
            {
                fileContent = new byte[0];
                return false;
            }
            //if (File.Exists(filePath))
            //    File.Delete(filePath);
            return true;
        }

        static void CloseConnection(ConnectionInfo ci)                     //CloseConnection
        {
            try
            {
                //lock (ci.sync)
                //{
                ci.SyncMutex.WaitOne();
                    ci.sync.TaskWork = false;
                ci.SyncMutex.ReleaseMutex();
                //}
                ci.Socket.Shutdown(SocketShutdown.Both);
                //ci.Socket.Disconnect(true);
                ci.Socket.Close(100);
                

                var date = DateTime.Now;

                OnUsersListChangedEvent(ci.Login, "", "", DateTime.Now, ci.Ip, false);

                lock (Connections) Connections.Remove(ci);
                if (ci.Login != "unknown")
                {
                    WriteToLog(ci.Login, ci.GroupId, ci.Oblast, ServerCategories.Login,
                               Rm.GetString("UserLeft"), date);
                    ShowNIcon(2000, Rm.GetString("UserLeft"), ci.Login, ToolTipIcon.Info);
                }
                //ci.Dispose();
            }
            catch (Exception ex)
            {
                string fullmess = ex.Message + ": " + ci.Login;
                if (fullmess.Length < 90)
                    WriteToLogEvent(fullmess, ServerCategories.SystemError, true);
            }

        }

        public static void Test()
        {
            //var tv = new DllUnmanagedWork("t-vips_analizator.dll", "T-Vips");
            //var res = RS.CallMethod("getstatus", new[] {"192.168.2.12"});
            var tv = new DllUnmanagedWork("t-vips_analizator.dll", "tv");
            //var res = tv.GetLog("192.168.2.249", "d:\\testl.csv");
            //var ret = t_vips_analizator.T_VIPS.CallMethod("getstatus", new[] {"192.168.2.11"});
            var res = tv.GetStatus("192.168.2.249");
        }

        static void DeviceLogDataResponse(string criteriaMessage, ConnectionInfo connection)
        {
            DateTime dt1 = DateTime.Now;
            var toSend = new StringBuilder();
            string[] requestResult = DBwork.GetDeviceLogList(criteriaMessage);
            if (requestResult.Length < 1)
            {
                toSend.Append("err:" + ClientExceptionMessage.EMPTY);
                connection.Socket.Send(Encoding.Default.GetBytes(toSend.ToString()));
                return;
            }
            if (requestResult[0].StartsWith("err:"))
            {
                connection.Socket.Send(Encoding.Default.GetBytes("err:" + ServerResponceTypes.INNER_ERROR));
                return;
            }
            
            foreach (string reqRes in requestResult)
                toSend.AppendLine(reqRes);
            var data2Send = Encoding.Default.GetBytes(toSend.ToString());

            var lenHeader = Encoding.Default.GetBytes("len:");
            var lenData = BitConverter.GetBytes(data2Send.Length);
            var lenToSend = new byte[lenData.Length + lenHeader.Length];
            Buffer.BlockCopy(lenHeader, 0, lenToSend, 0, lenHeader.Length);
            Buffer.BlockCopy(lenData, 0, lenToSend, lenHeader.Length, lenData.Length);

            connection.Socket.Send(lenToSend);
            connection.Socket.Send(data2Send);
            
            DateTime dt2 = DateTime.Now;
            TimeSpan ts = dt2 - dt1;
        }

        static void DeviceTreeResponse(ConnectionInfo connection)
        {
            if (!connection.IsAuthorized)
            {
                connection.Socket.Send(Encoding.Default.GetBytes("err:" + ServerResponceTypes.NOT_AUTHORIZED));
                WriteToLog(Rm.GetString("UnauthorizedRequest") + ". IP: " + connection.Ip, ServerCategories.Login);
                ShowNIcon(2000, Rm.GetString("Warning"), (Rm.GetString("UnauthorizedRequest") ?? "resources error") + ". IP: " + connection.Ip, ToolTipIcon.Warning);
                CloseConnection(connection);
            }
            var chk = SendDeviceTree(connection);
            if (chk == DoReturnString)
                return;

            if (chk != "ok")
            {
                ShowNIcon(2000, Rm.GetString("Warning"), chk.Substring(4), ToolTipIcon.Warning);
            }
        }

    }

    public delegate void UsersListChangedDelegate(string login, string access, string region, string loggedIn, string ip, bool added);

    public delegate void NewLogRecordDelegate(
        string login, string access, string region, string category, string eventName, DateTime date);

}
